{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9763887c-5c4f-4dba-9658-ad453acec9ab",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Created a chunk of size 125, which is longer than the specified 50\n",
      "Created a chunk of size 72, which is longer than the specified 50\n",
      "Created a chunk of size 72, which is longer than the specified 50\n",
      "Created a chunk of size 63, which is longer than the specified 50\n",
      "Created a chunk of size 52, which is longer than the specified 50\n",
      "Created a chunk of size 96, which is longer than the specified 50\n",
      "Created a chunk of size 51, which is longer than the specified 50\n",
      "Created a chunk of size 66, which is longer than the specified 50\n",
      "Created a chunk of size 105, which is longer than the specified 50\n",
      "Created a chunk of size 84, which is longer than the specified 50\n",
      "Created a chunk of size 78, which is longer than the specified 50\n",
      "Created a chunk of size 72, which is longer than the specified 50\n",
      "Created a chunk of size 66, which is longer than the specified 50\n",
      "Created a chunk of size 92, which is longer than the specified 50\n",
      "Created a chunk of size 58, which is longer than the specified 50\n",
      "Created a chunk of size 67, which is longer than the specified 50\n",
      "Created a chunk of size 73, which is longer than the specified 50\n",
      "Created a chunk of size 52, which is longer than the specified 50\n",
      "Created a chunk of size 61, which is longer than the specified 50\n",
      "Created a chunk of size 77, which is longer than the specified 50\n",
      "Created a chunk of size 82, which is longer than the specified 50\n",
      "Created a chunk of size 61, which is longer than the specified 50\n",
      "Created a chunk of size 52, which is longer than the specified 50\n",
      "Created a chunk of size 60, which is longer than the specified 50\n",
      "Created a chunk of size 51, which is longer than the specified 50\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一个文档切割\n",
      "page_content='蒂法介绍' metadata={'start_index': 1}\n",
      "page_content='蒂法·洛克哈特(日语:ティファ・ロックハート，Tifa Rokkuhāto，英语:Tifa' metadata={'start_index': 6}\n",
      "字符串切割\n",
      "page_content='蒂法介绍\n",
      "蒂法·洛克哈特(日语:ティファ・ロックハート，Tifa Rokkuhāto，英语:Tifa Lockhart)为电子游戏《最终幻想VII》及《最终幻想VII补完计划》相关作品中的虚构⻆ 色，由􏰀村哲也创作和设计，此后也在多个游戏中客串登场' metadata={'start_index': 1}\n",
      "按token来切割文档\n",
      "page_content='蒂法介绍\n",
      "蒂法·洛克哈特(日语:ティファ・ロックハート，Tifa Rokkuhāto，英语:Tifa Lockhart)为电子游戏《最终幻想VII》及《最终幻想VII补完计划》相关作品中的虚构⻆ 色，由􏰀村哲也创作和设计，此后也在多个游戏中客串登场。 2014年东京电玩展上，星名美津纪cosplay《最终幻想VII 降临之子》中的蒂法·洛克哈特 蒂法是克劳德的⻘梅竹⻢，两人同为尼布鲁海姆出身。在米德加经营作为反抗组织“雪崩”根 据地的酒馆“第七天堂”，并且是小有名气的招牌店员。擅⻓格斗，以拳套为武器。本传7年前 克劳德离开故乡从军时，曾许下约定“如果有危机时一定会保护她”。与爱丽丝相识之后，两 人成为好友。第一个察觉克劳德记忆混乱的人，后来协助精神崩溃的克劳德􏰁新找回真正的自 己。本传的大战结束后，依大家的期待在战后新生的米德加再次开设第七天堂(原第七天堂因 第柒区圆盘崩塌遭压毁)，同时也照顾一群受到星痕症候群折磨的孩子们。 蒂法被《纽约时报》称为“网络一代”的海报女郎，与劳拉·克罗夫特相比，她是电子游戏中坚 强、􏰂立和有吸引力的女性⻆色的典型代表。媒体普遍称赞其实力和外表，并称她为游戏世界 中最好的女性⻆色之一。 在《最终幻想VII》本传中，蒂法年龄20岁、身高167cm、生日5月3日、血型B型、出生地尼 布尔海姆。\n",
      "登场\n",
      "《最终幻想VII》 蒂法在《最终幻想VII》原版中首次亮相，是克劳德的⻘梅竹⻢、第七天堂酒吧的看板娘、极 端环境组织“雪崩”成员，该组织反抗巨型企业“神罗”因其大􏰃抽取魔晄用作动力能源。在注 意到克劳德的性格改变后，她说服克劳德加入雪崩，以密切关注他，并且跟随他追寻游戏中的 对手萨菲罗斯。她无法阻止克劳德被萨菲罗斯操纵，在他的精神崩溃后，她帮助克劳德康复， 并且两人意识到彼此间的相互感觉，最后与伙伴们一同击败了萨菲罗斯。[2] 在闪回中可知，儿时的蒂法一直是村中小孩的人气王。在母亲过世后，思念母亲的蒂法决定沿 着小路走到他们故乡尼布尔海姆附近的一座山上，认为这样就能⻅到过世的母亲，原本跟着蒂 法的其他小孩都在半路上因害怕而放弃，唯􏰂克劳德仍坚定的在后面跟随，希望能在危机时保 护蒂法。然而，他们俩都从山上跌落受伤，蒂法昏迷了一个星期，她的父亲认为克劳德对此负 有责任[3]，甚为严令禁止克劳德再接近蒂法，但蒂法反而从此更在意克劳德，两人成为要好 的玩伴。为了使自己变得更强大，克劳德最终选择离开尼布尔海姆，加入神罗，想要成为神罗 的精英战士“神罗战士”(SOLDIER)，但后来透露他主要是为了吸引蒂法的注意力。离开之 前，蒂法与克劳德约定，当蒂法处于困境之中时，克劳德会回来救她。从克劳德离开之后，蒂 法便开始留意神罗战士的消息，因为神罗战士都成为声名远播的知名人物，如果克劳德成为神 罗战士，他的活跃也会立刻传回尼布尔海姆。数年后，在萨菲罗斯摧毁了尼布尔海姆之后，克 劳德为了救蒂法，被萨菲罗斯刺至􏰁伤。蒂法被她的武术教练赞干带到安全地带，幸存下来， 最终到达米德加并遇⻅了“雪崩”的领导人巴雷特·华莱士。病愈后，蒂法加入了“雪崩”，为 了给家乡被毁一事报仇。一天，她在火⻋站遇到了从魔晄炉中逃出来、精神一片混乱的克劳 德，蒂法说服了他为巴雷特工作，以保证克劳德的安全以及和克劳德保持紧密关系。这是游戏 开始的地方。 在原版《最终幻想VII》中蒂法与爱丽丝关系友好，但会在某些时候争⻛吃醋，例如在神罗总 部营救爱丽丝时，蒂法及巴雷特等一行失手被擒，若克劳德选择关心爱丽丝的话蒂法的对话中 明显带有妒忌。在􏰁制版中虽然删去这段情节，但保留了这种关系。 在《最终幻想VII》的初稿中，蒂法是背景人物。她在“雪崩”中的作用是在幕后支持，在执 行任务后为所有人加油鼓劲，并且对克劳德有特别的关心。据推测，她的背上有一块大的疤'\n"
     ]
    }
   ],
   "source": [
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain.text_splitter import CharacterTextSplitter\n",
    "\n",
    "print(\"第一个文档切割\")\n",
    "#加载要切割的文档\n",
    "with open(\"kecheng/test.txt\", encoding=\"utf-8\") as f:\n",
    "    zuizhonghuanxiang = f.read()\n",
    "\n",
    "#初始化切割器\n",
    "text_splitter = RecursiveCharacterTextSplitter(\n",
    "    chunk_size=50,#切分的文本块大小，一般通过长度函数计算\n",
    "    chunk_overlap=20,#切分的文本块重叠大小，一般通过长度函数计算\n",
    "    length_function=len,#长度函数,也可以传递tokenize函数\n",
    "    add_start_index=True,#是否添加起始索引\n",
    ")\n",
    "\n",
    "text = text_splitter.create_documents([zuizhonghuanxiang])\n",
    "print(text[0])\n",
    "print(text[1])\n",
    "print(\"字符串切割\")\n",
    "#初始化切分器\n",
    "text_splitter = CharacterTextSplitter(\n",
    "    separator=\"。\",#切割的标志字符，默认是\\n\\n\n",
    "    chunk_size=50,#切分的文本块大小，一般通过长度函数计算\n",
    "    chunk_overlap=20,#切分的文本块重叠大小，一般通过长度函数计算\n",
    "    length_function=len,#长度函数,也可以传递tokenize函数\n",
    "    add_start_index=True,#是否添加起始索引\n",
    "    is_separator_regex=False,#是否是正则表达式\n",
    ")\n",
    "text = text_splitter.create_documents([zuizhonghuanxiang])\n",
    "print(text[0])\n",
    "\n",
    "print(\"按token来切割文档\")\n",
    "#初始化切分器\n",
    "text_splitter = CharacterTextSplitter.from_tiktoken_encoder(\n",
    "    chunk_size=4000,#切分的文本块大小，一般通过长度函数计算\n",
    "    chunk_overlap=30,#切分的文本块重叠大小，一般通过长度函数计算\n",
    ")\n",
    "\n",
    "text = text_splitter.create_documents([zuizhonghuanxiang])\n",
    "print(text[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "72f0db28-d98f-4f8f-8871-76f675c6c9f1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "代码文档切割： [Document(metadata={}, page_content='def hello_world():\\n    print(\"Hello, World!\")'), Document(metadata={}, page_content='#调用函数\\nhello_world()')]\n"
     ]
    }
   ],
   "source": [
    "# 代码文档切割\n",
    "from langchain.text_splitter import (\n",
    "    RecursiveCharacterTextSplitter,\n",
    "    Language,\n",
    ")\n",
    "\n",
    "#支持解析的编程语言\n",
    "#[e.value for e in Language]\n",
    "\n",
    "#要切割的代码文档\n",
    "PYTHON_CODE = \"\"\"\n",
    "def hello_world():\n",
    "    print(\"Hello, World!\")\n",
    "#调用函数\n",
    "hello_world()\n",
    "\"\"\"\n",
    "py_spliter = RecursiveCharacterTextSplitter.from_language(\n",
    "    language=Language.PYTHON,\n",
    "    chunk_size=50,\n",
    "    chunk_overlap=10,\n",
    ")\n",
    "python_docs = py_spliter.create_documents([PYTHON_CODE])\n",
    "print(\"代码文档切割：\", python_docs)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "5d17756f-ff22-4817-b5e4-802e528925aa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始文档长度: 2865 字符\n",
      "\n",
      "正在生成文档总结...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\chat_models\\openai.py:174: UserWarning: Unexpected type for token usage: <class 'NoneType'>\n",
      "  warnings.warn(f\"Unexpected type for token usage: {type(new_usage)}\")\n",
      "Warning: model not found. Using cl100k_base encoding.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "文档总结：\n",
      "蒂法·洛克哈特是《最终幻想VII》系列核心角色，克劳德的青梅竹马，经营“第七天堂”酒吧，以坚强性格与格斗能力闻名。她支持精神崩溃的克劳德，战后照顾星痕症候群患儿，亦活跃于多部衍生作品及跨界游戏中，如《降临之子》《王国之心II》，2015年成为手游《最终幻想 记录者》可玩角色。\n",
      "\n",
      "正在翻译文档...\n",
      "文档翻译：\n",
      "蒂法简介\n",
      "蒂法·洛克哈特（日语：ティファ・ロックハート，Tifa Rokkuhāto，英语：Tifa Lockhart）是电子游戏《最终幻想VII》及《最终幻想VII补完计划》相关作品中的虚构角色，由植松伸也创作和设计，此后也在多个游戏中客串登场。2014年东京电玩展上，星名美津纪扮演了《最终幻想VII 降临之子》中的蒂法·洛克哈特。蒂法是克劳德的青梅竹马，两人同为尼布尔海姆出身。在米德加经营作为反抗组织“雪崩”根据地的酒吧“第七天堂”，并且是小有名气的招牌店员。擅长格斗，以拳套为武器。本传7年前克劳德离开故乡从军时，曾许下约定“如果有危机时一定会保护她”。与爱丽丝相识之后，两人成为好友。第一个察觉克劳德记忆混乱的人，后来协助精神崩溃的克劳德重新找回真正的自己。本传的大战结束后，依大家的期待在战后新生的米德加再次开设第七天堂（原第七天堂因第七区魔晄炉崩塌遭压毁），同时也照顾一群受到星痕症候群折磨的孩子们。\n",
      "\n",
      "蒂法被《纽约时报》称为“网络一代”的海报女郎，与劳拉·克罗夫特相比，她是电子游戏中坚强、独立和有吸引力的女性角色的典型代表。媒体普遍称赞其实力和外表，并称她为游戏世界中最好的女性角色之一。\n",
      "\n",
      "在《最终幻想VII》本传中，蒂法年龄20岁、身高167cm、生日5月3日、血型B型、出生地尼布尔海姆。\n",
      "\n",
      "登场\n",
      "\n",
      "《最终幻想VII》\n",
      "蒂法在《最终幻想VII》原版中首次亮相，是克劳德的青梅竹马、第七天堂酒吧的看板娘、极端环境组织“雪崩”成员，该组织反抗巨型企业“神罗”因其大量抽取魔晄用作动力能源。在注意到克劳德的性格改变后，她说服克劳德加入雪崩，以密切关注他，并且跟随他追寻游戏中的对手萨菲罗斯。她无法阻止克劳德被萨菲罗斯操纵，在他的精神崩溃后，她帮助克劳德康复，并且两人意识到彼此间的相互感觉，最后与伙伴们一同击败了萨菲罗斯。\n",
      "\n",
      "在闪回中可知，儿时的蒂法一直是村中小孩的人气王。在母亲过世后，思念母亲的蒂法决定沿着小路走到他们故乡尼布尔海姆附近的一座山上，认为这样就能见到过世的母亲，原本跟着蒂法的其他小孩都在半路上因害怕而放弃，唯有克劳德仍坚定的在后面跟随，希望能在危机时保护蒂法。然而，他们俩都从山上跌落受伤，蒂法昏迷了一个星期，她的父亲认为克劳德对此负有责任，甚为严令禁止克劳德再接近蒂法，但蒂法反而从此更在意克劳德，两人成为要好的玩伴。为了使自己变得更强大，克劳德最终选择离开尼布尔海姆，加入神罗，想要成为神罗的精英战士“神罗战士”（SOLDIER），但后来透露他主要是为了吸引蒂法的注意力。离开之前，蒂法与克劳德约定，当蒂法处于困境之中时，克劳德会回来救她。从克劳德离开之后，蒂法便开始留意神罗战士的消息，因为神罗战士都成为声名远播的知名人物，如果克劳德成为神罗战士，他的活跃也会立刻传回尼布尔海姆。数年后，在萨菲罗斯摧毁了尼布尔海姆之后，克劳德为了救蒂法，被萨菲罗斯刺至重伤。蒂法被她的武术教练赞干带到安全地带，幸存下来，最终到达米德加并遇见了“雪崩”的领导人巴雷特·华莱士。病愈后，蒂法加入了“雪崩”，为了给家乡被毁一事报仇。一天，她在火车站遇到了从魔晄炉中逃出来、精神一片混乱的克劳德，蒂法说服了他为巴雷特工作，以保证克劳德的安全以及和克劳德保持紧密关系。这是游戏开始的地方。\n",
      "\n",
      "在原版《最终幻想VII》中蒂法与爱丽丝关系友好，但会在某些时候争风吃醋，例如在神罗总部营救爱丽丝时，蒂法及巴雷特等一行失手被擒，若克劳德选择关心爱丽丝的话蒂法的对话中明显带有嫉妒。在重制版中虽然删去这段情节，但保留了这种关系。\n",
      "\n",
      "在《最终幻想VII》的初稿中，蒂法是背景人物。她在“雪崩”中的作用是在幕后支持，在执行任务后为所有人加油鼓劲，并且对克劳德有特别的关心。据推测，她的背上有一块大的疤痕，是由克劳德造成的，并在事件发生时因巨大冲击而部分失忆。原版策划者之一的加藤正人提出了一个旨在暗示蒂法和克劳德发生性关系的场景，但被北濑佳范用一个变淡的色调所取代。野岛一成在接受采访时说，没有一个开发团队人员认为当时的场景会成为一个问题。\n",
      "\n",
      "《最终幻想VII补完计划》\n",
      "2005年，蒂法出现在CG电影《最终幻想VII 降临之子》中，故事发生在原版游戏剧情结束两年后。在其中，她试图给予克劳德情感上的支持，敦促克劳德放下他对自己施加的不必要的罪恶感。此外，她还照顾巴雷特的养女玛琳和克劳德在爱丽丝的教堂门口救下的孩子丹泽尔。在电影中，她与萨菲罗斯的其中一个思念体罗兹战斗，后来她帮助与被召唤的生物巴哈姆特战斗。编剧野岛一成将她在视频中的角色描述为“非常像任何被男人抛弃的女人”，并表示尽管他们不希望她显得笨拙，但他们也想描绘出从她受到克劳德的情感伤害出发。\n",
      "\n",
      "在视频的初稿中，她原本计划在当时的短片中扮演更重要的角色，该短片仅以蒂法，克劳德和几个孩子为主。\n",
      "\n",
      "蒂法也在前传游戏《最终幻想VII 危机之前》和《最终幻想VII 核心危机》以及OVA《最终幻想VII 最终命令》中登场。每次登场时，她的出现都与尼布尔海姆的毁灭有关。官方小说《通向微笑之路》中有专门的《蒂法篇》，讲述了原版游戏和降临之子两段之间的故事。从蒂法的角度出发，详细讲述了她如何在Edge City创建一个新的第七天堂酒吧，并试图坚持自己和克劳德的正常家庭观念，尽管克劳德逐渐开始逃避与他人接触。蒂法还短暂出现在游戏《最终幻想VII 地狱犬的挽歌》中，该游戏的剧情在降临之子故事一年后发生，她在游戏中帮助主角文森特·瓦伦丁捍卫星球，抵抗怪物欧米茄和“兵器”。她后来出现在游戏的结尾中，讨论着文森特的失踪。\n",
      "\n",
      "其他登场\n",
      "在《最终幻想VII补完计划》之外，蒂法还出现在格斗游戏《神佑擂台》中，作为可解锁的角色和可选的Boss。她后来出现在电子棋盘游戏《富豪街》中。在《王国之心II》中，她穿着自己在降临之子中的服装，寻找克劳德，然后与该系列的怪物“无心”战斗。她原本计划出现在原版《王国之心》的最终合辑中，但由于时间限制，工作人员选择改用萨菲罗斯。蒂法也是格斗游戏《最终幻想 纷争012》中的玩家角色之一，该游戏的角色来自各种《最终幻想》游戏。她身着《最终幻想VII》中的服装，但该玩家还可以使用她在《降临之子》中的服装和在尼布尔海姆出场时展示的第三种服装。游戏的第一版展示内容是在天野喜孝的美术原型基础上展现的另一种形式。在《小小大星球2》中，蒂法是可下载的角色模型。\n",
      "\n",
      "韩国歌手Ivy在2007年的歌曲“유혹의유혹의”（《诱惑奏鸣曲》）MV中扮演了角色。因为出现了《降临之子》中的战斗场面，在史克威尔艾尼克斯提出版权诉讼后，该视频被禁止在韩国电视上播出。\n",
      "\n",
      "2015年，蒂法作为可玩角色被添加到iOS游戏《最终幻想 记录者》中。\n",
      "\n",
      "⚡ 正在精炼文档，保留'marketing'和'Development'相关内容...\n",
      "文档精炼：\n",
      "{'text': '无相关内容', 'topics': 'marketing、Development'}\n"
     ]
    }
   ],
   "source": [
    "from langchain.schema import Document\n",
    "from langchain.chains import LLMChain\n",
    "from langchain.prompts import PromptTemplate\n",
    "# from langchain_community.llms import OpenAI\n",
    "from langchain_community.chat_models import ChatOpenAI\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain.chains.summarize import load_summarize_chain\n",
    "import os\n",
    "\n",
    "# 设置环境变量\n",
    "os.environ[\"OPENAI_API_KEY\"] = \"sk-4e88cf4db3e14894bafaff606d296610\"\n",
    "os.environ[\"OPENAI_API_BASE\"] = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    model=\"qwen-plus\",  # 使用通义千问 plus 模型\n",
    "    temperature=0.3,      # 温度为0，确保输出确定性\n",
    "    openai_api_key=os.environ[\"OPENAI_API_KEY\"],\n",
    "    openai_api_base=os.environ[\"OPENAI_API_BASE\"],\n",
    ")\n",
    "\n",
    "def summarize_document(content, token_limit=100):\n",
    "    \"\"\"总结文档内容\"\"\"\n",
    "    try:\n",
    "        # 创建文档对象\n",
    "        doc = Document(page_content=content)\n",
    "        \n",
    "        # 如果文档太长，先进行分割\n",
    "        text_splitter = RecursiveCharacterTextSplitter(\n",
    "            chunk_size=2000,\n",
    "            chunk_overlap=200,\n",
    "            length_function=len\n",
    "        )\n",
    "        \n",
    "        # 分割文档\n",
    "        docs = text_splitter.split_documents([doc])\n",
    "        \n",
    "        # 使用 map_reduce 方式进行总结\n",
    "        summary_chain = load_summarize_chain(\n",
    "            llm=llm,\n",
    "            chain_type=\"map_reduce\",\n",
    "            verbose=False\n",
    "        )\n",
    "        \n",
    "        # 执行总结\n",
    "        summary = summary_chain.run(docs)\n",
    "        \n",
    "        # 如果总结结果太长，进行精简\n",
    "        if len(summary) > token_limit:\n",
    "            refine_prompt = PromptTemplate(\n",
    "                template=\"请将以下内容精简到{token_limit}字以内:\\n\\n{text}\",\n",
    "                input_variables=[\"text\", \"token_limit\"]\n",
    "            )\n",
    "            refine_chain = LLMChain(llm=llm, prompt=refine_prompt)\n",
    "            summary = refine_chain.run(text=summary, token_limit=token_limit)\n",
    "        \n",
    "        return summary\n",
    "        \n",
    "    except Exception as e:\n",
    "        print(f\"总结文档时出错: {e}\")\n",
    "        return None\n",
    "\n",
    "def translate_document(content, target_language=\"chinese\"):\n",
    "    \"\"\"翻译文档内容\"\"\"\n",
    "    try:\n",
    "        # 创建翻译提示模板\n",
    "        translate_prompt = PromptTemplate(\n",
    "            template=\"将以下内容翻译成{language}，保持原意不变:\\n\\n{text}\",\n",
    "            input_variables=[\"text\", \"language\"]\n",
    "        )\n",
    "        \n",
    "        # 创建翻译链\n",
    "        translate_chain = LLMChain(llm=llm, prompt=translate_prompt)\n",
    "        \n",
    "        # 执行翻译\n",
    "        translation = translate_chain.run(text=content, language=target_language)\n",
    "        \n",
    "        return translation\n",
    "        \n",
    "    except Exception as e:\n",
    "        print(f\"翻译文档时出错: {e}\")\n",
    "        return None\n",
    "\n",
    "\"\"\"\n",
    "精炼文档，仅保留与指定主题相关的内容\n",
    "    参数:\n",
    "        document: 文档对象\n",
    "        topics: 主题关键词列表\n",
    "    返回: 精炼后的文本\n",
    "\"\"\"\n",
    "def refine_document(content, topics):\n",
    "    try:\n",
    "        refined_contents = []\n",
    "        \n",
    "        # 创建精炼提示模板\n",
    "        refine_prompt = ChatPromptTemplate.from_messages([\n",
    "                (\"system\", \"你是一个内容筛选助手，擅长根据主题筛选相关内容。\"),\n",
    "                (\"human\", \"\"\"\n",
    "请分析以下文本内容，仅保留与这些主题相关的内容：{topics}\n",
    "\n",
    "要求：\n",
    "1. 如果内容与任何主题都不相关，请返回\"无相关内容\"\n",
    "2. 保持原文的风格和格式\n",
    "3. 不要添加任何额外说明\n",
    "\n",
    "原文：\n",
    "{text}\n",
    "\n",
    "筛选后的内容：\n",
    "\"\"\")\n",
    "        ])\n",
    "        \n",
    "        # 创建精炼链\n",
    "        refine_chain = LLMChain(llm=llm, prompt=refine_prompt)\n",
    "            \n",
    "        # 将主题列表转换为字符串\n",
    "        topics_str = \"、\".join(topics)\n",
    "\n",
    "        refine_result = refine_chain.invoke({\"text\": content,\"topics\": topics_str})\n",
    "        \n",
    "        return refine_result\n",
    "        \n",
    "    except Exception as e:\n",
    "        print(f\"精炼文档时出错: {e}\")\n",
    "        return None\n",
    "\n",
    "def main():\n",
    "    # 读取文件内容\n",
    "    try:\n",
    "        with open(\"kecheng/test.txt\", \"r\", encoding=\"utf-8\") as f:\n",
    "            content = f.read()\n",
    "        \n",
    "        print(f\"原始文档长度: {len(content)} 字符\")\n",
    "        \n",
    "        # 总结文档\n",
    "        print(\"\\n正在生成文档总结...\")\n",
    "        summary = summarize_document(content, token_limit=100)\n",
    "        if summary:\n",
    "            print(\"文档总结：\")\n",
    "            print(summary)\n",
    "        \n",
    "        # 翻译文档\n",
    "        print(\"\\n正在翻译文档...\")\n",
    "        translation = translate_document(content, \"chinese\")\n",
    "        if translation:\n",
    "            print(\"文档翻译：\")\n",
    "            print(translation)\n",
    "\n",
    "        # 精炼文档\n",
    "        print(\"\\n⚡ 正在精炼文档，保留'marketing'和'Development'相关内容...\")\n",
    "        refine = refine_document(content, topics=[\"marketing\", \"Development\"])\n",
    "        if refine:\n",
    "            print(\"文档精炼：\")\n",
    "            print(refine)\n",
    "            \n",
    "    except FileNotFoundError:\n",
    "        print(\"错误：找不到文件 'kecheng/test.txt'\")\n",
    "    except Exception as e:\n",
    "        print(f\"处理文件时出错: {e}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "5fc4e4a8-d9e6-4815-a541-b974ea96fcb8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🔧 环境变量配置:\n",
      "OPENAI_API_KEY: sk-4e88cf4db3e14894b...\n",
      "OPENAI_API_BASE: https://dashscope.aliyuncs.com/compatible-mode/v1\n",
      "OPENAI_MODEL: qwen-plus\n",
      "✅ ChatOpenAI 初始化成功\n",
      "✅ 文档处理器初始化成功\n",
      "📄 原始文档长度: 2865 字符\n",
      "✅ 文档解析成功\n",
      "\n",
      "==================================================\n",
      "🔍 正在生成文档总结...\n",
      "📋 开始文档总结...\n",
      "📊 文档被分割成 4 个片段\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\chat_models\\openai.py:174: UserWarning: Unexpected type for token usage: <class 'NoneType'>\n",
      "  warnings.warn(f\"Unexpected type for token usage: {type(new_usage)}\")\n",
      "Warning: model not found. Using cl100k_base encoding.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "📝 初步总结长度: 166 字符\n",
      "⚡ 正在精简总结内容...\n",
      "📋 文档总结：\n",
      "蒂法·洛克哈特是《最终幻想VII》中的主要角色，克劳德的青梅竹马，经营“第七天堂”酒吧，也是“雪崩”成员。她擅长格斗，性格坚强独立，帮助克劳德恢复记忆，并在多部续作和跨界作品中登场，是游戏史上具有影响力的经典女性角色。\n",
      "\n",
      "==================================================\n",
      "🌐 正在翻译文档...\n",
      "🌐 开始文档翻译...\n",
      "📊 文档被分割成 4 个翻译片段\n",
      "🔄 正在翻译第 1/4 个片段...\n",
      "🔄 正在翻译第 2/4 个片段...\n",
      "🔄 正在翻译第 3/4 个片段...\n",
      "🔄 正在翻译第 4/4 个片段...\n",
      "✅ 翻译完成，总长度: 2892 字符\n",
      "📖 文档翻译：\n",
      "蒂法介绍\n",
      "蒂法·洛克哈特（日语：ティファ・ロックハート，Tifa Lockhart；英语：Tifa Lockhart）是电子游戏《最终幻想VII》及《最终幻想VII补完计划》相关作品中的虚构角色，由野村哲也创作和设计，此后也在多款游戏中客串登场。2014年东京电玩展上，星名美津纪曾cosplay《最终幻想VII 降临之子》中的蒂法·洛克哈特。\n",
      "\n",
      "蒂法是克劳德的青梅竹马，两人均出身于尼布尔海姆。她在米德加经营着一家名为“第七天堂”的酒馆，作为反抗组织“雪崩”的据点，同时她也是小有名气的招牌店员。蒂法擅长格斗，以拳套为武器。七年前，克劳德离开故乡参军时，曾许下诺言“在有危机时一定会保护她”。在认识爱丽丝后，两人成为好友。她是第一个察觉克劳德记忆混乱的人，并在后来帮助精神崩溃的克劳德重新找回真正的自己。正传大战结束后，应大家的期望，蒂法在战后重建的米德加再次开设了第七天堂（原第七天堂因第七区魔晄炉坠毁事故而被摧毁），同时照顾着一群受星痕症候群困扰的孩子们。\n",
      "\n",
      "蒂法被《纽约时报》称为“网络一代”的海报女郎，与劳拉·克罗夫特相比，她被视为电子游戏中坚强、独立且具有吸引力的女性角色的典型代表。媒体普遍称赞她的实力与外貌，并称她为游戏世界中最佳女性角色之一。\n",
      "\n",
      "在《最终幻想VII》正传中，蒂法年龄为20岁，身高167厘米，生日为5月3日，血型为B型，出生地为尼布尔海姆。\n",
      "\n",
      "登场\n",
      "\n",
      "登场\n",
      "《最终幻想VII》 蒂法在《最终幻想VII》原版中首次登场，是克劳德的青梅竹马、第七天堂酒吧的老板娘，同时也是极端环保组织“雪崩”的成员，该组织致力于反抗巨型企业“神罗”过度开采魔晄作为能源的行为。在注意到克劳德性格发生变化后，她劝说克劳德加入雪崩，以便能够近距离观察他，并跟随他一同对抗游戏中的主要敌人萨菲罗斯。她未能阻止克劳德被萨菲罗斯操控，在克劳德精神崩溃后帮助他恢复，并且两人意识到彼此之间的感情，最终与同伴们一起击败了萨菲罗斯。[2] 通过闪回剧情可知，年幼时的蒂法一直深受村中孩子们的欢迎。在母亲去世后，思念母亲的蒂法决定沿着小路前往他们故乡尼布尔海姆附近的一座山上，认为这样就能见到已故的母亲。原本跟随蒂法的其他孩子们都因害怕而在途中放弃了，唯有克劳德坚定地跟随在她身后，希望能在危机时刻保护她。然而，两人不慎从山上跌落并受了伤，蒂法昏迷了一周。她的父亲认为克劳德应对事故负责[3]，并严厉禁止克劳德再接近蒂法。但蒂法反而从此更加关注克劳德，两人成为了亲密的玩伴。为了让自己变得更强大，克劳德最终决定离开尼布尔海姆，加入神罗公司，成为一名神罗精英战士“神罗战士”（SOLDIER），而实际上，他这么做主要是为了引起蒂法的注意。离开之前，蒂法与克劳德约定：当蒂法陷入困境时，克劳德会回来拯救她。自克劳德离开后，蒂法开始关注神罗战士的消息，因为这些战士都成为了声名远播的人物，如果克劳德成为神罗战士，他的事迹也会很快传回尼布尔海姆。数年后，在萨菲罗斯摧毁了尼布尔海姆之后，克劳德为营救蒂法而被萨菲罗斯刺伤。蒂法被她的武术教练赞干救走并带到安全地带，幸存下来后前往米德加，并遇见了“雪崩”组织的领导人巴雷特·华莱士。康复后，蒂法加入了“雪崩”，为家乡的毁灭寻求复仇。某日，她在火车站遇到了从魔晄炉中逃出、精神混乱的克劳德，并说服他为巴雷特工作，以确保克劳德的安全并维持与他的密切关系。这也是游戏剧情的起点。\n",
      "\n",
      "在原版《最终幻想VII》中，蒂法与爱丽丝关系友好，但偶尔会因克劳德而产生嫉妒情绪。例如在神罗总部营救爱丽丝时，蒂法和巴雷特等人失手被擒，若克劳德选择关心爱丽丝，蒂法的对话中会明显流露出嫉妒之情。在重制版中虽然删去了这一情节，但保留了这种微妙的关系。\n",
      "\n",
      "在《最终幻想VII》最初的设定中，蒂法原本只是一个背景角色。她在“雪崩”中的作用主要是幕后支持，在执行任务后为所有人加油打气，并对克劳德表现出特别的关心。据推测，她的背上有一块较大的疤痕。\n",
      "\n",
      "伤痕是由克劳德造成的，并且在事件发生时因巨大冲击而部分失忆。[4] 原版策划者之一加藤正人曾提出一个旨在暗示蒂法与克劳德发生性关系的场景，但被北濑佳范以一个渐隐的色调所取代。岛一成在一次采访中表示，开发团队中没有人认为当时的那个场景会成为一个问题。[5]  \n",
      "《最终幻想VII补完计划》\n",
      "\n",
      "2005年，蒂法出现在CG电影《最终幻想VII 降临之子》中，故事设定在原版游戏剧情结束的两年后。在影片中，她试图给予克劳德情感上的支持，劝导克劳德放下他强加于自己的无谓罪恶感。此外，她还照顾巴雷特的养女玛琳，以及克劳德在爱丽丝的教堂门前救下的男孩丹泽尔。在电影中，她与萨菲罗斯的一个思念体罗兹展开战斗，并协助对抗召唤生物巴哈姆特。编剧岛一成形容她在影片中的角色“非常像任何被男人抛弃的女人”，并表示尽管他们不希望她显得笨拙，但也希望表现出她因克劳德的情感伤害而受到的影响。[6] 在影片的初稿中，她原本计划在当时的一部短片中扮演更重要的角色，该短片主要围绕蒂法、克劳德以及几个孩子展开。\n",
      "\n",
      "蒂法也出现在前传游戏《最终幻想VII 危机之前》和《最终幻想VII 核心危机》以及OVA《最终幻想VII 最终命令》中。每次登场，她的出现都与尼布尔海姆的毁灭有关。[2] 官方小说《通往微笑之路》中的《蒂法篇》讲述了原版游戏与《降临之子》之间的故事。从蒂法的视角出发，详细描述了她如何在Edge City建立一个新的第七天堂酒吧，并努力维持自己与克劳德之间正常的家庭观念，尽管克劳德逐渐开始回避与他人的接触。蒂法也在游戏《最终幻想VII 地狱犬的挽歌》中短暂登场，该游戏剧情发生在《降临之子》一年之后。在游戏中，她协助主角文森特·瓦伦丁捍卫星球，对抗怪物欧米茄和“兵器”。她后来出现在游戏结尾，与其他角色讨论着文森特的失踪。[2]\n",
      "\n",
      "除了《最终幻想VII补完计划》之外，蒂法还出现在格斗游戏《神佑擂台》中，作为可解锁角色和可选Boss。[7]她后来也出现在电子棋盘游戏《富豪街》中。在《王国之心II》中，蒂法穿着她在《降临之子》中的服装，寻找克劳德，并与该系列的怪物“无心”展开战斗。她原本计划出现在原版《王国之心》的最终合辑中，但由于时间限制，开发团队最终选择了萨菲罗斯代替。[8]蒂法也是格斗游戏《最终幻想 纷争012》中的可操作角色之一，该游戏汇集了来自各个《最终幻想》系列作品的角色。[9]她以《最终幻想VII》中的造型登场，但玩家也可以选择她在《降临之子》中的服装，以及她在尼布尔海姆出场时所穿的第三种服装。[10][11]游戏最初展示的版本是基于天野喜孝的美术原型设计的另一种风格。[12]在《小小大星球2》中，蒂法作为可下载的角色模型出现。[13] 韩国歌手Ivy在其2007年的歌曲“유혹의유혹의”（《诱惑奏鸣曲》）的MV中扮演了蒂法一角。由于MV中出现了《降临之子》中的战斗画面，史克威尔艾尼克斯提出版权诉讼后，该视频被禁止在韩国电视上播出。[14] 2015年，蒂法作为可玩角色被加入到iOS游戏《最终幻想 记录者》中。[15]\n",
      "\n",
      "==================================================\n",
      "⚡ 正在精炼文档，保留'marketing'和'Development'相关内容...\n",
      "⚡ 开始文档精炼...\n",
      "📊 文档被分割成 4 个精炼片段\n",
      "🔍 正在精炼第 1/4 个片段...\n",
      "🔍 正在精炼第 2/4 个片段...\n",
      "🔍 正在精炼第 3/4 个片段...\n",
      "🔍 正在精炼第 4/4 个片段...\n",
      "ℹ️ 没有找到与指定主题相关的内容\n",
      "🎯 精炼后的内容：\n",
      "没有找到与指定主题相关的内容\n"
     ]
    }
   ],
   "source": [
    "from langchain.schema import Document, HumanMessage, SystemMessage\n",
    "from langchain.prompts import PromptTemplate, ChatPromptTemplate\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain.chains.summarize import load_summarize_chain\n",
    "from langchain_community.chat_models import ChatOpenAI\n",
    "from langchain.chains import LLMChain\n",
    "import os\n",
    "\n",
    "class DocumentProcessor:\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        初始化文档处理器\n",
    "        使用ChatOpenAI替代Tongyi，保持与OpenAI兼容模式的连接\n",
    "        \"\"\"\n",
    "        # 设置环境变量（保持与原始Doctran代码相同的配置）\n",
    "        os.environ[\"OPENAI_API_KEY\"] = \"sk-4e88cf4db3e14894bafaff606d296610\"\n",
    "        os.environ[\"OPENAI_API_BASE\"] = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "        os.environ[\"OPENAI_API_TYPE\"] = \"open_ai\"\n",
    "        os.environ[\"OPENAI_DEPLOYMENT_ID\"] = \"qwen-plus\"\n",
    "        os.environ[\"OPENAI_MODEL\"] = \"qwen-plus\"\n",
    "        \n",
    "        # 验证环境变量设置\n",
    "        print(\"🔧 环境变量配置:\")\n",
    "        print(f\"OPENAI_API_KEY: {os.getenv('OPENAI_API_KEY')[:20]}...\")  # 只显示前20位\n",
    "        print(f\"OPENAI_API_BASE: {os.getenv('OPENAI_API_BASE')}\")\n",
    "        print(f\"OPENAI_MODEL: {os.getenv('OPENAI_MODEL')}\")\n",
    "        \n",
    "        # 初始化ChatOpenAI（使用聊天模型接口）\n",
    "        self.llm = ChatOpenAI(\n",
    "            model_name=\"qwen-plus\",           # 模型名称\n",
    "            openai_api_base=os.environ[\"OPENAI_API_BASE\"],\n",
    "            openai_api_key=os.environ[\"OPENAI_API_KEY\"],\n",
    "            temperature=0.3,                  # 较低的温度值使输出更稳定\n",
    "            max_tokens=2000,                  # 最大输出token数\n",
    "            streaming=False,                  # 非流式输出\n",
    "            timeout=30                        # 请求超时时间\n",
    "        )\n",
    "        \n",
    "        # 初始化文本分割器，用于处理长文档\n",
    "        self.text_splitter = RecursiveCharacterTextSplitter(\n",
    "            chunk_size=1200,        # 每个片段的最大字符数（稍微减小以适应token限制）\n",
    "            chunk_overlap=100,      # 片段之间的重叠字符数\n",
    "            length_function=len     # 使用Python内置的len函数计算长度\n",
    "        )\n",
    "        \n",
    "        print(\"✅ ChatOpenAI 初始化成功\")\n",
    "    \n",
    "    def parse(self, content):\n",
    "        \"\"\"\n",
    "        解析文档内容，类似于Doctran的parse方法\n",
    "        参数:\n",
    "            content: 原始文本内容\n",
    "        返回: Document对象\n",
    "        \"\"\"\n",
    "        return Document(page_content=content)\n",
    "    \n",
    "    def summarize(self, document, token_limit=100):\n",
    "        \"\"\"\n",
    "        总结文档内容\n",
    "        参数:\n",
    "            document: 文档对象\n",
    "            token_limit: 总结的最大长度（字数）\n",
    "        返回: 总结后的文本\n",
    "        \"\"\"\n",
    "        try:\n",
    "            print(\"📋 开始文档总结...\")\n",
    "            \n",
    "            # 分割长文档为多个片段\n",
    "            docs = self.text_splitter.split_documents([document])\n",
    "            print(f\"📊 文档被分割成 {len(docs)} 个片段\")\n",
    "            \n",
    "            # 使用map_reduce方式进行总结（适合长文档）\n",
    "            summary_chain = load_summarize_chain(\n",
    "                llm=self.llm,\n",
    "                chain_type=\"map_reduce\",  # 先分别总结每个片段，再总结所有片段总结\n",
    "                verbose=False\n",
    "            )\n",
    "            \n",
    "            # 执行总结\n",
    "            summary_result = summary_chain.invoke(docs)\n",
    "            summary_text = summary_result['output_text']\n",
    "            \n",
    "            print(f\"📝 初步总结长度: {len(summary_text)} 字符\")\n",
    "            \n",
    "            # 如果总结结果过长，进行进一步精简\n",
    "            if len(summary_text) > token_limit:\n",
    "                print(\"⚡ 正在精简总结内容...\")\n",
    "                summary_text = self._refine_summary(summary_text, token_limit)\n",
    "            \n",
    "            return summary_text\n",
    "            \n",
    "        except Exception as e:\n",
    "            print(f\"❌ 总结文档时出错: {e}\")\n",
    "            return None\n",
    "    \n",
    "    def _refine_summary(self, summary_text, token_limit):\n",
    "        \"\"\"\n",
    "        精简总结内容到指定长度\n",
    "        参数:\n",
    "            summary_text: 原始总结文本\n",
    "            token_limit: 目标长度限制\n",
    "        返回: 精简后的文本\n",
    "        \"\"\"\n",
    "        try:\n",
    "            # 使用ChatPromptTemplate创建对话式提示\n",
    "            refine_prompt = ChatPromptTemplate.from_messages([\n",
    "                (\"system\", \"你是一个专业的文档总结助手，擅长将内容精炼到指定长度。\"),\n",
    "                (\"human\", \"\"\"\n",
    "请将以下总结内容精简到{token_limit}字以内，保留最核心的信息和关键点：\n",
    "\n",
    "{text}\n",
    "\n",
    "请只返回精简后的内容，不要添加任何额外说明：\n",
    "\"\"\")\n",
    "            ])\n",
    "            \n",
    "            # 创建LLMChain\n",
    "            refine_chain = LLMChain(llm=self.llm, prompt=refine_prompt)\n",
    "            \n",
    "            # 执行精简\n",
    "            refined_result = refine_chain.invoke({\n",
    "                \"text\": summary_text,\n",
    "                \"token_limit\": token_limit\n",
    "            })\n",
    "            \n",
    "            return refined_result['text'].strip()\n",
    "            \n",
    "        except Exception as e:\n",
    "            print(f\"❌ 精简总结时出错: {e}\")\n",
    "            return summary_text  # 返回原始文本作为fallback\n",
    "    \n",
    "    def translate(self, document, language=\"chinese\"):\n",
    "        \"\"\"\n",
    "        翻译文档内容\n",
    "        参数:\n",
    "            document: 文档对象\n",
    "            language: 目标语言\n",
    "        返回: 翻译后的文本\n",
    "        \"\"\"\n",
    "        try:\n",
    "            print(\"🌐 开始文档翻译...\")\n",
    "            \n",
    "            # 分割文档以便逐段翻译\n",
    "            docs = self.text_splitter.split_documents([document])\n",
    "            print(f\"📊 文档被分割成 {len(docs)} 个翻译片段\")\n",
    "            \n",
    "            translations = []\n",
    "            \n",
    "            # 创建翻译提示模板\n",
    "            translate_prompt = ChatPromptTemplate.from_messages([\n",
    "                (\"system\", \"你是一个专业的翻译助手，擅长准确翻译技术文档。\"),\n",
    "                (\"human\", \"\"\"\n",
    "请将以下内容准确翻译成{language}，保持专业术语的正确性和语气的连贯性：\n",
    "\n",
    "{text}\n",
    "\n",
    "请只返回翻译后的内容：\n",
    "\"\"\")\n",
    "            ])\n",
    "            \n",
    "            # 创建翻译链\n",
    "            translate_chain = LLMChain(llm=self.llm, prompt=translate_prompt)\n",
    "            \n",
    "            # 逐段翻译\n",
    "            for i, doc in enumerate(docs):\n",
    "                print(f\"🔄 正在翻译第 {i+1}/{len(docs)} 个片段...\")\n",
    "                \n",
    "                translation_result = translate_chain.invoke({\n",
    "                    \"text\": doc.page_content,\n",
    "                    \"language\": language\n",
    "                })\n",
    "                \n",
    "                translations.append(translation_result['text'].strip())\n",
    "            \n",
    "            # 合并所有翻译片段\n",
    "            final_translation = \"\\n\\n\".join(translations)\n",
    "            print(f\"✅ 翻译完成，总长度: {len(final_translation)} 字符\")\n",
    "            \n",
    "            return final_translation\n",
    "            \n",
    "        except Exception as e:\n",
    "            print(f\"❌ 翻译文档时出错: {e}\")\n",
    "            return None\n",
    "    \n",
    "    def refine(self, document, topics):\n",
    "        \"\"\"\n",
    "        精炼文档，仅保留与指定主题相关的内容\n",
    "        参数:\n",
    "            document: 文档对象\n",
    "            topics: 主题关键词列表\n",
    "        返回: 精炼后的文本\n",
    "        \"\"\"\n",
    "        try:\n",
    "            print(\"⚡ 开始文档精炼...\")\n",
    "            \n",
    "            # 分割文档以便逐段筛选\n",
    "            docs = self.text_splitter.split_documents([document])\n",
    "            print(f\"📊 文档被分割成 {len(docs)} 个精炼片段\")\n",
    "            \n",
    "            refined_contents = []\n",
    "            \n",
    "            # 创建精炼提示模板\n",
    "            refine_prompt = ChatPromptTemplate.from_messages([\n",
    "                (\"system\", \"你是一个内容筛选助手，擅长根据主题筛选相关内容。\"),\n",
    "                (\"human\", \"\"\"\n",
    "请分析以下文本内容，仅保留与这些主题相关的内容：{topics}\n",
    "\n",
    "要求：\n",
    "1. 如果内容与任何主题都不相关，请返回\"无相关内容\"\n",
    "2. 保持原文的风格和格式\n",
    "3. 不要添加任何额外说明\n",
    "\n",
    "原文：\n",
    "{text}\n",
    "\n",
    "筛选后的内容：\n",
    "\"\"\")\n",
    "            ])\n",
    "            \n",
    "            # 创建精炼链\n",
    "            refine_chain = LLMChain(llm=self.llm, prompt=refine_prompt)\n",
    "            \n",
    "            # 将主题列表转换为字符串\n",
    "            topics_str = \"、\".join(topics)\n",
    "            \n",
    "            # 逐段筛选内容\n",
    "            for i, doc in enumerate(docs):\n",
    "                print(f\"🔍 正在精炼第 {i+1}/{len(docs)} 个片段...\")\n",
    "                \n",
    "                refine_result = refine_chain.invoke({\n",
    "                    \"text\": doc.page_content,\n",
    "                    \"topics\": topics_str\n",
    "                })\n",
    "                \n",
    "                refined_content = refine_result['text'].strip()\n",
    "                \n",
    "                # 只保留有相关内容的片段\n",
    "                if refined_content and refined_content != \"无相关内容\":\n",
    "                    refined_contents.append(refined_content)\n",
    "            \n",
    "            # 合并所有精炼后的内容\n",
    "            if refined_contents:\n",
    "                final_content = \"\\n\\n\".join(refined_contents)\n",
    "                print(f\"✅ 精炼完成，保留内容长度: {len(final_content)} 字符\")\n",
    "                return final_content\n",
    "            else:\n",
    "                print(\"ℹ️ 没有找到与指定主题相关的内容\")\n",
    "                return \"没有找到与指定主题相关的内容\"\n",
    "                \n",
    "        except Exception as e:\n",
    "            print(f\"❌ 精炼文档时出错: {e}\")\n",
    "            return None\n",
    "\n",
    "def main():\n",
    "    \"\"\"\n",
    "    主函数，演示如何使用DocumentProcessor类替代Doctran\n",
    "    \"\"\"\n",
    "    # 初始化文档处理器\n",
    "    processor = DocumentProcessor()\n",
    "    print(\"✅ 文档处理器初始化成功\")\n",
    "    \n",
    "    try:\n",
    "        # 读取文件内容\n",
    "        with open(\"kecheng/test.txt\", \"r\", encoding=\"utf-8\") as f:\n",
    "            content = f.read()\n",
    "        \n",
    "        print(f\"📄 原始文档长度: {len(content)} 字符\")\n",
    "        \n",
    "        # 解析文档（类似于Doctran的parse）\n",
    "        document = processor.parse(content)\n",
    "        print(\"✅ 文档解析成功\")\n",
    "        \n",
    "        # 1. 总结文档\n",
    "        print(\"\\n\" + \"=\"*50)\n",
    "        print(\"🔍 正在生成文档总结...\")\n",
    "        summary = processor.summarize(document, token_limit=100)\n",
    "        if summary:\n",
    "            print(\"📋 文档总结：\")\n",
    "            print(summary)\n",
    "        else:\n",
    "            print(\"❌ 总结失败\")\n",
    "        \n",
    "        # 2. 翻译文档\n",
    "        print(\"\\n\" + \"=\"*50)\n",
    "        print(\"🌐 正在翻译文档...\")\n",
    "        translation = processor.translate(document, language=\"chinese\")\n",
    "        if translation:\n",
    "            print(\"📖 文档翻译：\")\n",
    "            print(translation)\n",
    "        else:\n",
    "            print(\"❌ 翻译失败\")\n",
    "        \n",
    "        # 3. 精炼文档\n",
    "        print(\"\\n\" + \"=\"*50)\n",
    "        print(\"⚡ 正在精炼文档，保留'marketing'和'Development'相关内容...\")\n",
    "        refined = processor.refine(document, topics=[\"marketing\", \"Development\"])\n",
    "        if refined:\n",
    "            print(\"🎯 精炼后的内容：\")\n",
    "            print(refined)\n",
    "        else:\n",
    "            print(\"❌ 精炼失败\")\n",
    "            \n",
    "    except FileNotFoundError:\n",
    "        print(\"❌ 错误：找不到文件 'kecheng/test.txt'\")\n",
    "    except Exception as e:\n",
    "        print(f\"❌ 处理文件时出错: {e}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # 安装所需依赖：pip install langchain langchain-community\n",
    "    main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3729f02-edcb-45fb-8fbe-5dedc0605774",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "rag_learn",
   "language": "python",
   "name": "rag_learn"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
